package userscopelogic

import (
	"context"

	"go-zero-admin/apps/system/cmd/rpc/internal/svc"
	"go-zero-admin/apps/system/cmd/rpc/syspb"
	"go-zero-admin/apps/system/model"
	"go-zero-admin/pkg/constant"

	"github.com/Masterminds/squirrel"
	"github.com/duke-git/lancet/v2/convertor"
	set "github.com/duke-git/lancet/v2/datastructure/set"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/pkg/errors"
	"github.com/zeromicro/go-zero/core/fx"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/mr"
)

type GetUserDataScopeLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetUserDataScopeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserDataScopeLogic {
	return &GetUserDataScopeLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetUserDataScopeLogic) GetUserDataScope(in *syspb.GetUserDataScopeReq) (*syspb.GetUserDataScopeResp, error) {
	// 错误，什么数据都查不出来 userId=-1
	if in.GetUserId() == 0 {
		return &syspb.GetUserDataScopeResp{DeptIds: nil, UserId: -1}, nil
	}
	userId := in.GetUserId()

	// 是否超管
	// 全部权限userId=0
	if l.IsAdminUser(userId) {
		return &syspb.GetUserDataScopeResp{DeptIds: nil, UserId: 0}, nil
	}

	// 获取当前用户的角色
	roleList := l.getRoleByUid(userId)
	// 如果用户没有角色则无权限，只查询自己的
	if len(roleList) == 0 {
		return &syspb.GetUserDataScopeResp{DeptIds: nil, UserId: userId}, nil
	}

	// 先判断用户是否有全部数据权限的角色，如果有直接返回，避免重复查询
	var roleAllDataScopeList []*syspb.SysRole
	fx.From(func(source chan<- interface{}) {
		for _, v := range roleList {
			source <- v
		}
	}).Filter(func(item interface{}) bool {
		role := item.(*model.SysRole)
		if role.DataScope == constant.DataScopeAll {
			return true
		}
		return false
	}).ForEach(func(item interface{}) {
		roleAllDataScopeList = append(roleAllDataScopeList, item.(*syspb.SysRole))
	})
	if len(roleAllDataScopeList) > 0 {
		return &syspb.GetUserDataScopeResp{DeptIds: nil, UserId: 0}, nil
	}

	// 查询出用户部门id和所有部门信息
	var deptId int64
	var deptList []*model.SysDept
	if err := mr.Finish(func() (err error) {
		// 获取用户数据
		sysUser, err := l.svcCtx.SysUserModel.FindOne(l.ctx, userId)
		if err != nil {
			return err
		}
		// 上面已处理过超级管理员或者全部数据权限的角色了，如果非该俩种角色并且用户部门id不存在，则查询自己创建的权限
		if sysUser.DeptId == 0 {
			return errors.New("数据错误")
		}
		deptId = sysUser.DeptId
		return
	}, func() (err error) {
		// 获取部门信息
		deptList, err = l.svcCtx.SysDeptModel.FindAll(l.ctx, l.svcCtx.SysDeptModel.RowBuilder(), "")
		if err != nil {
			return err
		}
		return
	}); err != nil {
		// 不管报错还是部门id不存在，则只查询出自己的权限
		return &syspb.GetUserDataScopeResp{DeptIds: nil, UserId: userId}, nil
	}

	// 获取角色对应数据权限
	// 如果用户有多个角色，就整合处理部门权限
	// 如果用户又有部门权限，又有仅本人数据权限，则其二者关系是and 查既属于部门的又属于个人创建的
	deptAuthIdSet := set.NewSet[int64]()
	var userAuthId int64
	for _, role := range roleList {
		switch role.DataScope {
		case constant.DataScopeCustomize: // 自定数据权限
			roleDeptDbList, err := l.svcCtx.SysRoleDeptModel.FindAll(l.ctx, l.svcCtx.SysRoleDeptModel.RowBuilder().
				Where(squirrel.Eq{"role_id": role.Id}), "")
			if err != nil || len(roleDeptDbList) == 0 {
				continue
			}

			fx.From(func(source chan<- interface{}) {
				for _, v := range roleDeptDbList {
					source <- v
				}
			}).Map(func(item interface{}) interface{} {
				roleDept := item.(*model.SysRoleDept)
				return roleDept.DeptId
			}).ForEach(func(item interface{}) {
				deptAuthIdSet.Add(item.(int64))
			})
		case constant.DataScopeDept: // 本部门数据权限
			deptAuthIdSet.Add(deptId)
		case constant.DataScopeDeptAndSon: // 本部门及以下数据权限
			deptAuthIdSet.Add(deptId)

			// 查询出son数据
			if len(deptList) == 0 {
				continue
			}

			deptSonList := l.getDeptSonByPid(deptId, deptList)
			if len(deptSonList) == 0 {
				continue
			}

			fx.From(func(source chan<- interface{}) {
				for _, v := range deptSonList {
					source <- v
				}
			}).Map(func(item interface{}) interface{} {
				dept := item.(*model.SysDept)
				return dept.DeptId
			}).ForEach(func(item interface{}) {
				deptAuthIdSet.Add(item.(int64))
			})
		case constant.DataScopeOwn: // 仅本人数据权限
			userAuthId = userId
		}
	}

	// 如果是自定义数据权限没有选择部门数据，则只查询自己创建的
	if userAuthId == 0 && len(deptAuthIdSet.Values()) == 0 {
		return &syspb.GetUserDataScopeResp{DeptIds: nil, UserId: userId}, nil
	}

	return &syspb.GetUserDataScopeResp{
		DeptIds: deptAuthIdSet.Values(),
		UserId:  userAuthId,
	}, nil
}

// GetDataScopeBuilder 处理数据权限拼接SQL
func (l *GetUserDataScopeLogic) GetDataScopeBuilder(userId int64, deptIdField string, userIdField string) []any {
	dataScopeResp, _ := l.GetUserDataScope(&syspb.GetUserDataScopeReq{UserId: userId})
	deptAuthIds := dataScopeResp.GetDeptIds()
	userAuthId := dataScopeResp.GetUserId()

	//  如果用户又有部门权限，又有仅本人数据权限，则其二者关系是and 查既属于部门的又属于个人创建的
	var conditionList []any

	if len(deptAuthIds) > 0 {
		condition := squirrel.Eq{deptIdField: deptAuthIds}
		conditionList = append(conditionList, condition)
	}

	if userAuthId != 0 {
		condition := squirrel.Eq{userIdField: userAuthId}
		conditionList = append(conditionList, condition)
	}

	return conditionList
}

// GetMenuDataScopeBuilder 处理菜单数据权限
func (l *GetUserDataScopeLogic) GetMenuDataScopeBuilder(userId int64, menuIdField string, userIdField string) []any {
	var conditionList []any

	// 如果不存在用户id，则数据权限查询不出任何数据
	if userId == 0 {
		condition := squirrel.Eq{userIdField: -1}
		conditionList = append(conditionList, condition)
		return conditionList
	}

	// 是否超管
	if l.IsAdminUser(userId) {
		return conditionList
	}

	// 获取用户的角色信息 如果无角色信息只查询自己创建的
	roleIds := l.getRoleIdsByUid(userId)
	if len(roleIds) == 0 {
		condition := squirrel.Eq{userIdField: userId}
		conditionList = append(conditionList, condition)
		return conditionList
	}

	// 获取角色对应的菜单id
	enforcer := l.svcCtx.AdapterCasbin.SetCtxSession(l.ctx, nil).Enforcer
	var menuIds []int64
	fx.From(func(source chan<- interface{}) {
		for _, v := range roleIds {
			source <- v
		}
	}).Map(func(item interface{}) interface{} {
		menuIdList := enforcer.GetFilteredPolicy(0, convertor.ToString(item))
		return menuIdList
	}).ForEach(func(item interface{}) {
		for _, v := range item.([][]string) {
			mid, _ := convertor.ToInt(v[1])
			menuIds = append(menuIds, mid)
		}
	})

	// 如果角色无菜单，则只查询自己创建的
	if len(menuIds) == 0 {
		condition := squirrel.Eq{userIdField: userId}
		conditionList = append(conditionList, condition)
		return conditionList
	}

	// 如果角色有菜单，就查询查询出菜单和自己创建的 or的关系
	condition := squirrel.Or{squirrel.Eq{menuIdField: menuIds}, squirrel.Eq{userIdField: userId}}
	conditionList = append(conditionList, condition)

	return conditionList
}

// GetRoleDataScopeBuilder 获取角色数据权限
func (l *GetUserDataScopeLogic) GetRoleDataScopeBuilder(userId int64, roleIdField string, userIdField string) []any {
	var conditionList []any

	// 如果不存在用户id，则数据权限查询不出任何数据
	if userId == 0 {
		condition := squirrel.Eq{userIdField: -1}
		conditionList = append(conditionList, condition)
		return conditionList
	}

	// 是否超管
	if l.IsAdminUser(userId) {
		return conditionList
	}

	// 判断数据权限，获取其权限范围内的部门Id
	// 角色管理数据权限特殊，不完全适用于角色权限控制范围，只处理部门下人员的角色，不处理仅本人数据权限
	dataScopeResp, _ := l.GetUserDataScope(&syspb.GetUserDataScopeReq{UserId: userId})
	deptAuthIds := dataScopeResp.GetDeptIds()
	userAuthId := dataScopeResp.GetUserId()

	// 如果该用户是全部数据权限 则查询出全部角色信息
	if len(deptAuthIds) == 0 && userAuthId == 0 {
		return conditionList
	}

	// 权限角色集合
	roleIdSet := set.NewSet[int64]()

	// 先判断自己本身是否有角色，如果有则加入其中
	// 获取当前用户的角色
	userRoleIds := l.getRoleIdsByUid(userId)
	// 加入自己的角色
	if len(userRoleIds) > 0 {
		for _, v := range userRoleIds {
			roleIdSet.Add(v)
		}
	}

	// 该用户没有部门权限，则只查询自己的角色或自己创建的角色
	if len(deptAuthIds) == 0 {
		if len(roleIdSet.Values()) > 0 {
			condition := squirrel.Or{squirrel.Eq{roleIdField: roleIdSet.Values()}, squirrel.Eq{userIdField: userId}}
			conditionList = append(conditionList, condition)
		} else {
			condition := squirrel.Eq{userIdField: userId}
			conditionList = append(conditionList, condition)
		}
		return conditionList
	}

	// 如果该用户有部门权限
	userDbList, err := l.svcCtx.SysUserModel.FindAll(l.ctx, l.svcCtx.SysUserModel.RowBuilder().
		Where(squirrel.Eq{"dept_id": deptAuthIds}), "")
	// 如果是自定义部门权限，但是自定义部门下无人员角色，只查询自己的角色或自己创建的角色
	if err != nil || len(userDbList) == 0 {
		if len(roleIdSet.Values()) > 0 {
			condition := squirrel.Or{squirrel.Eq{roleIdField: roleIdSet.Values()}, squirrel.Eq{userIdField: userId}}
			conditionList = append(conditionList, condition)
		} else {
			condition := squirrel.Eq{userIdField: userId}
			conditionList = append(conditionList, condition)
		}
		return conditionList
	}

	// 如果有子部门人员角色，则获取其所有子部门人员的角色Id
	// 获取所有用户Id
	var userIds []int64
	fx.From(func(source chan<- interface{}) {
		for _, v := range userDbList {
			source <- v
		}
	}).Map(func(item interface{}) interface{} {
		return item.(*model.SysUser).Id
	}).ForEach(func(item interface{}) {
		userIds = append(userIds, item.(int64))
	})

	// 查询关联角色规则，获取用户关联角色id
	enforcer := l.svcCtx.AdapterCasbin.SetCtxSession(l.ctx, nil).Enforcer
	fx.From(func(source chan<- interface{}) {
		for _, v := range userIds {
			source <- v
		}
	}).Map(func(item interface{}) interface{} {
		groupPolicy := enforcer.GetFilteredGroupingPolicy(0, convertor.ToString(item))
		return groupPolicy
	}).ForEach(func(item interface{}) {
		for _, v := range item.([][]string) {
			val, _ := convertor.ToInt(v[1])
			roleIdSet.Add(val)
		}
	})

	// 如果该用户没有角色，部门下所有用户也无角色，只查询自己创建的
	if len(roleIdSet.Values()) == 0 {
		condition := squirrel.Eq{userIdField: userId}
		conditionList = append(conditionList, condition)
		return conditionList
	}

	condition := squirrel.Or{squirrel.Eq{roleIdField: roleIdSet.Values()}, squirrel.Eq{userIdField: userId}}
	conditionList = append(conditionList, condition)

	return conditionList
}

// 根据pid获取部门子类
func (l *GetUserDataScopeLogic) getDeptSonByPid(deptId int64, deptList []*model.SysDept) []*model.SysDept {
	var children []*model.SysDept
	for _, v := range deptList {
		if v.ParentId != deptId {
			continue
		}

		children = append(children, v)

		child := l.getDeptSonByPid(v.DeptId, deptList)
		if len(child) == 0 {
			continue
		}

		children = append(children, child...)
	}

	return children
}

// 根据用户id获取角色信息
func (l *GetUserDataScopeLogic) getRoleByUid(userId int64) []*model.SysRole {
	roleIds := l.getRoleIdsByUid(userId)

	var roles []*model.SysRole
	if len(roleIds) > 0 {
		roleDbList, _ := l.svcCtx.SysRoleModel.FindAll(l.ctx, l.svcCtx.SysRoleModel.RowBuilder().
			Where(squirrel.Eq{"id": roleIds}), "")
		roles = roleDbList
	}

	return roles
}

// 根据用户id获取角色ids
func (l *GetUserDataScopeLogic) getRoleIdsByUid(userId int64) []int64 {
	// 查询关联角色规则
	enforcer := l.svcCtx.AdapterCasbin.SetCtxSession(l.ctx, nil).Enforcer
	groupPolicy := enforcer.GetFilteredGroupingPolicy(0, convertor.ToString(userId))

	var roleIds []int64
	for _, v := range groupPolicy {
		val, _ := convertor.ToInt(v[1])
		roleIds = append(roleIds, val)
	}

	return roleIds
}

// IsAdminUser 判断是否是Admin
func (l *GetUserDataScopeLogic) IsAdminUser(userId int64) bool {
	if userId == 0 {
		return false
	}

	// 是否超管
	// 获取无需验证权限的用户id
	uidList := l.svcCtx.Config.SystemCustom.NotCheckAuthUserIds
	if slice.Contain(uidList, userId) {
		return true
	}

	// 获取用户角色判断是否超级管理员
	// 查询关联角色规则
	// 获取无需验证权限的用户id
	enforcer := l.svcCtx.AdapterCasbin.SetCtxSession(l.ctx, nil).Enforcer
	rids := l.svcCtx.Config.SystemCustom.NotCheckAuthRoleIds
	groupPolicy := enforcer.GetFilteredGroupingPolicy(0, convertor.ToString(userId))
	// 得到角色id的切片
	for _, v := range groupPolicy {
		val, _ := convertor.ToInt(v[1])
		if slice.Contain(rids, val) {
			return true
		}
	}
	return false
}
